/*
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 *
 * This file is part of Harmattan SmashMiner OpenGL game application.
 * 
 * Harmattan SmashMiner OpenGL Example Application version 1.0.0
 *  
 * Latest update: 15.4.2011
 *
 * The Harmattan SmashMiner OpenGL example application demonstrates how to use 
 * the OpenGL ES in Harmattan devices.
 *
 * This example is provided as a starting point for 3rd party
 * developers (you) to ease the implementation of OpenGL based 
 * games. Ideas, parts of code or methodologies that this
 * example application uses can be freely used without restrictions.
 *
 * See file README.txt how to build and compile this example application
 * in the Harmattan SDK environment. 
 * 
 * See file INSTALL.txt to find out what is required to run this
 * application and how to install the application to the device or
 * alternatively in QEMU-emulator.
 *
 * The LICENSE.txt file explains the license this software is provided
 * with. A copy of the same license is included in this source file.
 * 
 */

/*
 * Copyright (C) 2011 by Nokia Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/** @mainpage SmashMiner OpenGL ES Game Example
*
* @section intro Introduction
*
* The Harmattan SmashMiner OpenGL example application demonstrates how to use
* the OpenGL ES in Harmattan devices.
*
* This example is provided as a starting point for 3rd party
* developers (you) to ease the implementation of OpenGL based
* games. Ideas, parts of the code or methodologies that this
* example application uses can be freely used without restrictions.
*
* Note the existence of the following directories under the \c smashminer project folder:
* - \c qml/smashminer
* -- Contains the QML sources
* - \c qml/smashminer/pics
* -- Contains the pictures
* - \c sounds
* -- Contains the sound effect files
* - \c doc
* -- Contains the documentation
*
* \section secTutorials Tutorials
*
* \ref addingEnemies
*
* \ref changingGameBalance
*
* \ref replacingSoundEffect
*
* \ref addNewSoundEffect
*
* \ref addGameMenu
*
* \ref removeGameMenu
*
* \section term Terminology
*
* \subsection ssecMiner miner
*
* \e miner is an instance of \c SpaceCraft class guided by player. Sometimes
* miner is referred as a \e ship, a \e spaceship or a \e spacecraft.
*
* \subsection ssecShipMode ship mode
*
* Ship mode for the miner. the miner spaceship is either in a \e ball mode or in a \e cone mode. In the ball mode
* miner can break asteroids and in the code mode miner can collect minerals.
*/

/*! \page addingEnemies Adding new enemies and dropping existing enemies
 *
 * \section secAddingEnemies Adding new enemies
 *
 * Instructions for adding new enemy game objects to the game.
 * These instructions assume OpenGL ES 2 is used for rendering game objects.
 *
 * \subsection secGameWidget Changing the GameWidget class 
 *
 * - Define an enemy object graphics in a \c GameWidget class
 *   - OpenGL graphics are defined in file \c gamewidget_graphics.cpp
 *   - See \c GameWidget::initMissile() for an example
 *   - Call graphics initialization from \c GameWidget::initObjects().
 *
 * - Paint the enemy
 *   - Create an OpenGL paint method for the new enemy object in \c GameWidget
 *       - OpenGL ES 2 specific paint medhods are implemented in file \c gamewidget_GLes2.cpp
 *       - See \c GameWidget::GLes2PaintMissile for an example 
 *   - Call the paint method from \c GameWidget::GLes2Paint() 
 *
 * \subsection secGameObject Creating a game object class and setting level properties
 *
 * - Define the enemy object class and its properties
 *   - New game objects are derived from \c GameObject class
 *   - If needed, \c RotatingObject class provides properties for rotating the game object, for example
 *     \c Missile class
 *   - New enumerations and constants are needed to create a new \c GameObject type, see 
 *     \c GameObject
 * - Add enemy information to level data (if necessary - some enemies may appear in consequense of 
 *   other game events such as missiles fired by UFOs)
 *       - Create a field in \c ::GameLevelData structure
 *       - Set a value for field for each level in \c GameLevel::createData(). 
 *
 * \subsection secGameEngine Changing the GameEngine class
 *
 * - Add enemies to a game engine
 *   - Trigger more enemies at randomly selected level progress points
 *       - Create a trigger list for a new enemy, for example \c GameEngine::triggerUfo.
 *       - Initialize the trigger list in \c GameEngine::initNextLevel()
 *   - Create instances of enemy objects in \c GameEngine::moreGameObjects() 
 *       - Populate the game with game objects regarding to trigger lists
 *       - Append new enemies to \c GameEngine::enemyList
 *   - Handle movement
 *       - Advance new enemy objects in \c GameEngine::advanceGameObjects()
 *   - Handle collision detection if extra functionality is needed
 *       - Optional since \c GameEngine::detectCollisions() goes through the enemy list and detects 
 *         collisions between the miner and the enemy objects
 *   - Optional extra features may be implemented in \c GameEngine::controlGameEvents() 
 *       - For example UFO shooting missiles
 *
 * \section droppingEnemies Dropping existing enemies
 *
 * Instructions for dropping existing enemy game objects from the game.
 *
 * \subsection secDisableEnemy Disable existing enemies
 *
 * Easiest method to disable existing enemies in game is to
 * set amount of enemy types being removed to 0 in \c GameLevel::createData()
 * 
 * \subsection secRemoveEnemy Remove an existing enemy
 *
 * \subsubsection ssecRemoveLevelData Removing level data
 *
 * - Remove the data field from game level data \c GameLevelData regarding the enemy being removed.
 * - Remove populating of the level data for the data field from \c GameLevel::createData().
 *
 * \subsubsection ssecRemoveEnemiesGameEngine Changing the GameEngine
 *
 * - Remove triggering of enemies
 *   - Remove the initialising trigger list from \c GameEngine::initNextLevel()
 *   - It is a good idea to remove also the obsoleted trigger list from  \c GameEngine header
 *
 * - Remove populating game level with the enemy type in \c GameEngine::moreGameObjects()
 *      - Remove trigger list handling and appending enemies to \c GameEngine::enemyList
 *
 * - Remove movement handling from \c GameEngine::advanceGameObjects()
 *
 * - Remove collision detection handling from \c GameEngine::detectCollisions()
 *
 * - Remove possible additional features implemented for the enemy type from
 *   \c GameEngine::controlGameEvents() 
 *      - Such as UFO shooting missiles
 *          - When removing UFO, remove also the missile logic
 *
 * \subsubsection ssecRemoveEnemyGameWidget Changing the GameWidget class
 *
 * Remove the obsoleted graphics definitions and painting methods.
 *  - Remove graphics definitions (for example \c GameWidget::initMissile()) from \c gamewidget_graphics.cpp
 *  - Remove graphics initialization call from \c GameWidget::initObjects()
 *  - Remove the paint method from \c GameWidget
 *      - OpenGL ES 2 specific paint methods are implemented in \c gamewidget_GLes2.cpp file, for example \c GameWidget::GLes2PaintMissile
 *  - Remove the paint method call from \c GameWidget::GLes2Paint()
 *
 * \subsubsection ssecRemoveEnemyGameObject Removing enemy game object
 *
 * - Remove the obsoleted game object
 * - Remove the obsoleted \c GameObjectType and other enumerations and constants, see \c
 *   GameObject
 *
 * \subsection secRemovingBlackHole Removing the black hole
 *
 * \subsubsection ssecRemovingBlackHole_GameWidget Removes the black hole specific OpenGL painting 
 *
 * - There are shader programs specified to handle effects such as black hole rendering
 *   - When removing the black hole enemy the black hole data is no more needed in the shader
 *   programs
 *   - for example in vsrcConstantMaterialEff3D:
 *   \code
 *   uniform mediump vec4 bhdata4;
 *   \endcode
 *   - The code of shader programs is in \c GameWidget::GLes2Initialize() in 
 *     \c gamewidget_GLes2.cpp 
 *   - When the black hole is removed the following shader programs become obsolete
 *       - \c GameWidget::programConstanMaterialEffect3D
 *       - \c GameWidget::programConstantMaterialSpecularEffect3D
 *       - \c GameWidget::programVariableMaterial3D
 *       - \c GameWidget::programParticlesEffect2D
 *       - \c GameWidget::programTextureEffects3D
 *   - \c GameWidget::programBackgroundEffect2D is used for rendering the black hole and bomb effects
 *   - When removing the black hole data field from shader program code, remove the
 *     data field also from the shader program class
 *   - When removing the black hole, the following shader program classes become obsolete:
 *       - \c spConstantMaterialEff3D
 *       - \c spVariableMaterialEff3D 
 *       - \c spParticlesEff2D
 *       - \c spTextureEff3D
 * - Every paint method has a black hole specific code block
 *    - See the \c paintWithBlackHole code blocks
 *
 * \subsubsection ssecRemovingBlackHole_engine Changing the GameEngine
 *
 * - \c BlackHole instance is used with \c GameEngine::blackhole pointer.
 * - \c GameEngine::doBlackHoleEffect() becomes obsolete.
 * - Remove \c GameLevelData::blackhole and usage in \c GameLevel::createData().
 */
 
/*! \page changingGameBalance Changing the Game Balance and Enemy Logic

This tutorial is about changing the progress and difficulty of the game (how many lives the player has, how often the enemies appear, how fast the enemies move and so on) and about changing the enemy 
object logic (locating, movement, collision detection and so on).

\section changeNumberOfLives Changing the Number of Lives of the Player

Changing the number of lives is the most basic thing to do when changing progress of the game.
The number of lives is set at \c gameengine.h header at \c enum \c ::MinersAtStartup.
\code
...
enum {
    /// Amount of miners at the game start.
    MinersAtStartup = 5
};
...
\endcode
Value for \c ::MinersAtStartup can be set between 1-9.

\section changeAppearanceOfEnemies Changing how Often Enemies Appear

The \c struct GameLevelData is used to provide the basic frame for the
progress of each game level.  Events at certain level are happening
randomly but the probability is controlled by the GameLevelData.  The
GameLevelData struct contains variables for appearances, speed and
other variables of the GameObjects at the certain game level.

Each level has a certain maximum progress value and a current progress
value. The progress values are initialized in \c GameLevel::nextLevel()
(see the progress points explained in the method
documetation). The current progress value is incremented when collectibles
are picked up (see \c GameEngine::handleMinerCollectibleCollision())
or objects are blasted off with a bomb (see \c
GameEngine::applyBombDestruction()). Adding new game objects is
managed with trigger lists (for example \c GameEngine::triggerUfo). Each
trigger list contains pseudo randomly selected points(values) in the
progress of the level. See \c GameEngine::initNextLevel()
documentation for an example and \c GameEngine::moreGameObjects() for
example of populating the level with the game objects.

One other major thing affecting the game balance is the enemy logic. See section \ref enemyLogic how to change it.

For example the amount of UFO enemies is set by the following variable at the GameLevelData:
\code
...
    /// Amount of UFOs in level
    int ufos;
...
\endcode

There is a \c case at the method GameLevel::createData for each game level where the GameLevelData is initialised:
\code
...
    switch (levelNumber) {
    case 1:
...
\endcode
GameLevel::createData is called by the GameLevel::nextLevel method.

For example changing the appearance of the \c UFO enemies for level 1 is done by changing the \c data.ufos value:
\code
...
    switch (levelNumber) {
    case 1:
...
        data.ufos = 1;
...
\endcode
When setting \c data.ufos \c = \c 0 then there are no UFO enemies appearing in the first level.

 * \section enemyLogic  Changing the enemy logic
 *
 * Instructions for altering the logic for enemy game objects.
 *
 * \subsection secCollisionDetection
 *
 * Collisions are detected in \c GameEngine::detectCollisions().
 *
 * \subsection secEnemyLogic_BlackHole Black Hole
 *
 * Currently there can be only one black hole instance at once. The black hole instance
 * is managed through the \c GameEngine::blackhole pointer.
 *
 * \subsubsection secEnemyLogic_BlackHole_Amount Adjusting the amount of black holes
 *
 * The amount of the black holes per level is set with level data in \c GameLevelData::blackholes.
 * The following level specific properties can also be adjusted for the black hole:
 * - \c GameLevelData::blackholeSpeed
 * - \c GameLevelData::blackholeRadius
 *
 * \subsubsection secEnemyLogic_BlackHole_Location Setting the startup location and speed for the black holes
 *
 * The \c BlackHole object is instantiated in \c GameEngine::moreGameObjects() 
 * according to the \c GameEngine::currentLevelProgress. After instantiating the \c BlackHole,
 * the start location and the direction vector are calculated using \c GameEngine::getStartParams() 
 * and the velocity is set using the direction vector and \c GameLevelData::blackholeSpeed.
 *
 * \subsubsection secEnemyLogic_BlackHole_Destroying Disabling the black hole destruction with a bomb
 *
 * When the player activates a bomb, the on-screen black hole is also destroyed (set to state ''dead'' 
 * for possible rewind mode playback) in GameEngine::applyBombDestruction(). The black hole destruction
 * is disabled by removing the black hole check:
 *
 * \code
 * if (blackhole && blackhole->isAlive()) {
 *   ...
 * }
 * \endcode
 *
 * This applies also on destroying other enemies with a bomb.
 *
 * \subsubsection secEnemyLogic_BlackHole_Gravitation Altering black hole gravitation force
 *
 * To modify the way black hole affects on other objects, see 
 * \c GameEngine::doBlackHoleEffect(). Currently the black hole sucks in other objects.
 * In an another game, the black hole concept can be used for example as reversed force.
 *
 * \subsection secEnemyLogic_UFO UFO
 *
 * UFOs are populated to the game screen accorging to \c GameLevelData::ufos. 
 * Multiple UFO's can be moving on the game screen simultaneously. 
 *
 * The UFO objects are guided in \c GameEngine::advanceGameObjects() by calling 
 * \c Ufo::doUfoSteering(). To change UFO movement logic see the \c Ufo::doUfoSteering()
 * documentation.
 *
 * UFOs shoot missiles towards the player.
 * For guiding missiles see \ref secEnemyLogic_Missiles. 
 * \subsection secEnemyLogic_Missiles Missiles
 *
 * UFOs shoot missiles towards the miner. The following three game level specific parameters define
 * the way missiles behave:
 * - \c GameLevelData::missileSpeed 
 * - \c GameLevelData::missileIncidence 
 * - \c GameLevelData::missileGuidance
 *
 * UFOs are fired with \c GameEngine::fireMissile() according to the pseudo-random value and the
 * \c GameLevelData::missileIncidence value in \c GameEngine::controlGameEvents(). By adjusting the
 * \c GameLevelData::missileIncidence value, it is defined how rapidly missiles are fired. It is
 * also possible to change the logic for missile firing to \c GameEngine::controlGameEvents().
 *
 * \c GameEngine calls 
 * \c GameEngine::steerMissile() to calculate the direction vector for missile guidance.
 * The actual missile guidance according to the direction vector takes place at
 * \c Missile::setDirection() (called from \c Missile::adjustDirection()). In this method the
 * missile guiding logic can be redefined.
 */


#include <QtGui/QApplication>
#include <QtDeclarative/QDeclarativeContext>

//#include <QGLFormat>

#include "gameappviewer.h"
#include "gamesettings.h"

#include <QTimer>
#include <QDebug>
#include <QTranslator>
#include <QDesktopWidget>
#include <QGraphicsObject>
#include <QStyle>

#include <iostream>

#include<QX11Info>
#include<X11/Xlib.h>
#include<X11/Xatom.h>

//////////////////////////////////////////////////////////////////////////


void showUsage();
void setCannotMinimize(QWidget *);
void setDontAnimate(QWidget *);

Q_DECL_EXPORT  int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // detect commandline options:
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    int c;
    int iValue;

    while ((c = getopt(argc, argv, "bchi:lo:s:y:")) != -1)
        switch (c) {
        case 'b':
            GameEngine::commandlineShowBlackHole = true;
            qDebug() << "commandline option -b detected.";
            break;

        case 'c':
            GameEngine::commandlineShowComet = true;
            qDebug() << "commandline option -c detected.";
            break;

        case 'h':
            showUsage();
            break;

        case 'i':
            iValue = atoi(optarg);

            if (iValue < 1)
                iValue = 1;

            qDebug() << "commandline option -i detected with lives at start level " << iValue;
            GameEngine::commandlineStartLives = iValue;
            break;

        case 'l':
            GameEngine::commandlineUseLocalFiles = true;
            qDebug() << "commandline option -l detected.";
            break;

        case 'o':
            iValue = atoi(optarg);

            if (iValue < 0)
                iValue = 0;

            qDebug() << "commandline option -o detected with bombs at start level " << iValue;
            GameEngine::commandlineStartBombs = iValue;
            break;

        case 's':
            iValue = atoi(optarg);

            if (iValue < 1)
                iValue = 1;

            qDebug() << "commandline option -s detected with start level " << iValue;
            GameEngine::commandlineStartLevel = iValue;
            break;

        case 'y':
            iValue = atoi(optarg);

            if (iValue < 0)
                iValue = 0;

            qDebug() << "commandline option -y detected with hyperjumps at start level " << iValue;
            GameEngine::commandlineStartHyperjumps = iValue;
            break;

        case '?':
            if (optopt == 'c')
                qFatal("Option -%c requires an argument.", optopt);
            else if (isprint (optopt))
                qFatal("Unknown option `-%c'.", optopt);
            else
                qFatal("Unknown option character `\\x%x'.", optopt);
        }

    QString locale = QLocale::system().name();
    QTranslator translator;
    /* the ":/" is a special directory Qt uses to
       * distinguish resources;
       * NB this will look for a filename matching locale + ".qm";
       * if that's not found, it will truncate the locale to
       * the first two characters (e.g. "en_GB" to "en") and look
       * for that + ".qm"; if not found, it will look for a
       * smashminer.qm file; if not found, no translation is done
       */
    if (translator.load("ts/smashminer_"+locale, ":/"))
        app.installTranslator(&translator);
    else
        qDebug() << "No translation file found!";

    GameAppViewer viewer;

    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape);

    // define a contextproperty which we can use at qml side
    // for switching the display mode into opengl game mode.

    QDeclarativeContext *context = viewer.rootContext();
    context->setContextProperty("gameAppViewer", &viewer);
    context->setContextProperty("gameSettings", viewer.getSettings());
    context->setContextProperty("soundEngine", viewer.getSoundEngine());

    const QRect geom = QApplication::desktop()->availableGeometry();
    viewer.setGeometry(geom);
    context->setContextProperty("viewWidth", geom.width());
    context->setContextProperty("viewHeight", geom.height());

    // set a default value for titleHeight.
    // see GameAppViewer::getTitleHeight().

    context->setContextProperty("titleHeight", 0);

    viewer.setSource(QUrl("qrc:qml/smashminer/main.qml"));

    QObject::connect(&viewer, SIGNAL(notifyResize(const QVariant&, const QVariant&)),
                     viewer.rootObject(), SLOT(onResize(const QVariant&, const QVariant&)));

    QObject::connect(&viewer, SIGNAL(gameOver()), viewer.rootObject(), SLOT(gameOver()));

    QObject::connect(&viewer, SIGNAL(newHighScore(const QVariant&, const QVariant&)),
                     viewer.rootObject(), SLOT(newHighScore(const QVariant&, const QVariant&)));

    viewer.setWindowTitle("SmashMiner");

    GameWidget *gameWidget = viewer.initGameWidget();

#ifdef SMASHMINER_CONFIG_SET_STARTVIEW_FULLSCREEN
    viewer.showFullScreen();
#else   // SMASHMINER_CONFIG_SET_STARTVIEW_FULLSCREEN
    viewer.showMaximized();
#endif  // SMASHMINER_CONFIG_SET_STARTVIEW_FULLSCREEN

    // in Harmattan devices prevent the "leave the view"
    // gesture usage in the game view (but leave it active
    // in the QML start view) -> so the player must switch
    // to the QML start view before leaving the game.

    setCannotMinimize(gameWidget);

    viewer.startMusic();

    gameWidget = NULL;

    const int retval = app.exec();

    qDebug() << "SmashMiner exiting with code " << retval;

    return retval;
}

//
// disable animations
//
void setDontAnimate(QWidget *w)
{
    long data = 1;
    Atom atom = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_NO_ANIMATIONS", false);
    XChangeProperty(QX11Info::display(), w->effectiveWinId(), atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1);

}


void setCannotMinimize(QWidget *w)
{
    long data = 1;
    Atom atom = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_CANNOT_MINIMIZE", false);
    XChangeProperty(QX11Info::display(), w->effectiveWinId(), atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1);

}

void showUsage()
{
    std::cout << "SmashMiner command line options:\n"
        << "-b\tShow black hole on startup\n"
        << "-c\tShow comet on startup\n"
        << "-l\tUse local files (no installation needed for running)\n"
        << "-h\tShow help (this text)\n"
        << "-sN\tSet start level where N is the level number\n"
        << "-iN\tSet number of lives at start, where N is the number of lives\n"
        << "-oN\tSet number of bombs at start, where N is the number of bombs\n"
        << "-yN\tSet number of hyperjumps at start, where N is the number of hyperjumps\n";
    exit(0);
}
